home *** CD-ROM | disk | FTP | other *** search
/ MacHome 2000 March / MacHome CD (March 2000).iso / pc / Games / Bolo 0.99.2 / Documentation / Sample Code / BoloInfoPacket.c
C/C++ Source or Header  |  1994-03-12  |  7KB  |  198 lines

  1. /* Sample code to access Bolo's game infomation packet interface */
  2. /* (C) 1993 Stuart Cheshire <cheshire@cs.stanford.edu>
  3. /* Unix C file -- tabs are 8 spaces -- compile with gcc for best results */
  4. /* run with "a.out <port-number>" to listen on a UDP port for games starting */
  5. /* run with "a.out <port-number> <hostname>" to ping a running Bolo game and
  6.    then wait for replies. */
  7. /* run with "a.out <port-number> <subnet-broadcast-address> to locate ALL
  8.    Bolo games running within a particular IP broadcast domain. */
  9. /* There is no reliability here -- this is just to illustrate how to send and
  10.    receive the packets. You may wish to do retransmissions and timeouts. */
  11.  
  12. #include <stdio.h>
  13. #include <sys/types.h>
  14. #include <sys/socket.h>
  15. #include <netinet/in.h>
  16. #include <netdb.h>
  17. #include <sys/time.h>
  18.  
  19. /* Bolo uses little-endian byte ordering -- like the BBC micro's 6502
  20. processor, 80x86 series, Acorn Risk Machine, and DEC computers, and
  21. unlike Macs and Suns. The following macro should convert to and from
  22. local byte ordering on any unix machine. I don't know a better way to
  23. do it, but a good compiler will optimize it out anyway. */
  24. #define netshort(X) (htons(X) >> 8 | htons(X) << 8)
  25.  
  26. /* Macs count time since Midnight, 1st Jan 1904. Unix counts from 1970.
  27.    This value adjusts for the 66 years and 17 leap-days difference. */
  28. #define TIME_ADJUST (((1970 - 1904) * 365 + 17) * 24 * 60 * 60)
  29.  
  30. /* Also note the following two bugs in Bolo 0.99 and 0.99.1:
  31.  
  32. 1. The GameID was intended to be simply an opaque identifier to
  33. distinguish games. Hence I did not make Bolo put the components in
  34. canonical network byte order, since IDs were not supposed to be
  35. disassembled, but just compared for equality or inequality. However, I
  36. changed my mind and decided that it is useful (or at least interesting)
  37. to know when and where a game started. So, for now, the game timestamp is
  38. in the wrong byte order. This will be fixed in a future version of Bolo.
  39.  
  40. 2. The Mac system call "GetDateTime" returns the time in LOCAL time,
  41. not GMT. Bolo neglects to convert this time to GMT before sending it
  42. over the network, resulting in the game start time being given in local
  43. time for the Mac that started it. There is no way at present to convert
  44. this time to a real time, except if you happen to know what time zone
  45. the offending Mac is in. This will be fixed in a future version of Bolo. */
  46.  
  47. /* Convert Bolo's pascal string to C string so we can work with it easily */
  48. static void PtoCStr(unsigned char *string)
  49.     {
  50.     int i, len = string[0];
  51.     for (i=0; i<len; i++) string[i] = string[i+1];
  52.     string[len] = 0;
  53.     }
  54.  
  55. typedef struct { u_char c[36]; } u_char36;
  56. typedef u_char  BYTE;
  57. typedef u_short WORD;
  58.  
  59. typedef struct
  60.     {
  61.     BYTE signature[4];   /* 'Bolo' */
  62.     BYTE versionH;         /* 0      */
  63.     BYTE versionL;         /* 0x99   */
  64.     BYTE spare;         /* 0      */
  65.     BYTE type;         /* 13 for request, 14 for response */
  66.     } BOLOHEADER;
  67.  
  68. typedef struct
  69.     {
  70.     struct in_addr firstmachine;
  71.     u_long start_time;
  72.     } GAMEID;
  73.  
  74. typedef struct
  75.     {
  76.     BOLOHEADER h;
  77.  
  78.     u_char36 mapname;    /* Pascal string (first byte is length)         */
  79.     GAMEID gameid;         /* 8 byte unique ID for game (combination       */
  80.                  /* of starting machine address & timestamp)     */
  81.     BYTE gametype;         /* Game type (1, 2 or 3: open, tourn. & strict) */
  82.     BYTE allow_mines;    /* 0x80 for normal hidden mines                 */
  83.                  /* 0xC0 for all mines visible                   */
  84.     BYTE allow_AI;         /* 0 for no AI tanks, 1 for AI tanks allowed    */
  85.     BYTE spare1;         /* 0                                            */
  86.     long start_delay;    /* if non zero, time until game starts, (50ths) */
  87.     long time_limit;     /* if non zero, time until game ends, (50ths)   */
  88.  
  89.     WORD num_players;    /* number of players                            */
  90.     WORD free_pills;     /* number of free (neutral) pillboxes           */
  91.     WORD free_bases;     /* number of free (neutral) refuelling bases    */
  92.     BYTE has_password;   /* non-zero if game has password set            */
  93.     BYTE spare2;         /* 0                                            */
  94.     } INFO_PACKET;
  95.  
  96. static void sendquery(int s, char **argv)
  97.     {
  98.     static const BOLOHEADER h = { { "Bolo" }, 0, 0x99, 0, 13 };
  99.     struct sockaddr_in rmtaddr;
  100.     rmtaddr.sin_family      = AF_INET;
  101.     rmtaddr.sin_port        = htons(atoi(argv[1]));
  102.     rmtaddr.sin_addr.s_addr = inet_addr(argv[2]);
  103.     if (rmtaddr.sin_addr.s_addr == -1)
  104.         {
  105.            struct hostent *hp = gethostbyname(argv[2]);
  106.            if (!hp) { fprintf(stderr, "%s: unknown host\n", argv[2]); exit(1); }
  107.            rmtaddr.sin_family = hp->h_addrtype;
  108.            bcopy(hp->h_addr, (char *) &rmtaddr.sin_addr, hp->h_length);
  109.         }
  110.  
  111.            sendto(s, &h, sizeof(h), 0, &rmtaddr, sizeof(rmtaddr));
  112.     }
  113.  
  114. static void printip(struct in_addr a)
  115.     {
  116.     printf("%d.", a.S_un.S_un_b.s_b1);
  117.     printf("%d.", a.S_un.S_un_b.s_b2);
  118.     printf("%d.", a.S_un.S_un_b.s_b3);
  119.     printf("%d" , a.S_un.S_un_b.s_b4);
  120.     }
  121.  
  122. int main(int argc, char **argv)
  123.     {
  124.     int s;
  125.     struct sockaddr_in lcladdr;
  126.     u_short localport = 0;
  127.  
  128.        if (argc != 2 && argc != 3)
  129.         {
  130.         fprintf(stderr, "Usage: %s <local port>\n", argv[0]);
  131.         fprintf(stderr, "Usage: %s <remote port> <remote machine>\n", argv[0]);
  132.         exit(1);
  133.         }
  134.  
  135.     /* Open socket */
  136.     s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  137.     if (s < 0) { perror("socket"); exit(1); }
  138.  
  139.     /* Bind socket to local UDP port number */
  140.     lcladdr.sin_family      = AF_INET;
  141.     if (argc==2) lcladdr.sin_port = htons(atoi(argv[1]));
  142.     else         lcladdr.sin_port = 0;
  143.     lcladdr.sin_addr.s_addr = INADDR_ANY;
  144.     if (bind(s, &lcladdr, sizeof(lcladdr)) < 0) { perror("bind"); exit(1); }
  145.     if (argc==3) sendquery(s, argv);
  146.  
  147.     /* Receive the data */
  148.     while(1)
  149.         {
  150.         time_t timenow, gametime;
  151.         char *timestring, *ptr;
  152.         INFO_PACKET info;
  153.         struct sockaddr_in from;
  154.         int fromlen = sizeof(from);
  155.         int packetlen = recvfrom(s, &info, sizeof(info), 0, &from, &fromlen);
  156.         if (packetlen != sizeof(info)) continue;
  157.         if (info.h.signature[0] != 'B'  ||
  158.             info.h.signature[1] != 'o'  ||
  159.             info.h.signature[2] != 'l'  ||
  160.             info.h.signature[3] != 'o'  ||
  161.             info.h.versionL     != 0x99 ||
  162.             info.h.type         != 14   ) continue;
  163.  
  164.         PtoCStr(info.mapname.c);
  165.         info.num_players = netshort(info.num_players);
  166.         info.free_pills  = netshort(info.free_pills);
  167.         info.free_bases  = netshort(info.free_bases);
  168.         time(&timenow);
  169.         gametime = ntohl(info.gameid.start_time) - TIME_ADJUST;
  170.  
  171.         printf("%s", ctime(&timenow));
  172.  
  173.                printf("Bolo player at ");
  174.         printip(from.sin_addr);
  175.         printf(" on map \"%s\" running since\n", info.mapname.c);
  176.  
  177.         ptr = timestring = asctime(gmtime(&gametime));
  178.         while (*ptr && *ptr != '\n') ptr++;
  179.         *ptr = 0;
  180.         printf("%s (local time for Mac at ", timestring);
  181.         printip(info.gameid.firstmachine);
  182.         printf(")\n");
  183.  
  184.         printf("Options: ");
  185.         switch(info.gametype)
  186.             {
  187.             case 1: printf("Open Game"); break;
  188.             case 2: printf("Tournament"); break;
  189.             case 3: printf("Strict Tournament"); break;
  190.             }
  191.         if (info.allow_mines == 0x80) printf(", Hidden mines");
  192.         if (info.allow_AI) printf(", AI tanks allowed");
  193.         if (info.has_password) printf(", Password set");
  194.         printf("\nPlayers in game:%2d, Neutral pillboxes:%2d, Neutral Refueling Bases:%2d\n\n", info.num_players, info.free_pills, info.free_bases);
  195.         fflush(stdout);
  196.         }
  197.     }
  198.